/**
*  \file kwpserver.c
*
*  \brief
*
*  \author Michael J. Hohmann  <michael.hohmann@linde-mh.de>
*
*
*
@startuml
[*]-->kwpserver_state_receiving
kwpserver_state_receiving --> kwpserver_state_pending      : message received
kwpserver_state_pending   --> kwpserver_state_transmitting : service done
kwpserver_state_pending   --> kwpserver_state_receiving    : no response
kwpserver_state_transmitting --> kwpserver_state_receiving : message transmitted
@enduml
*/

/*===============================================[ private includes  ]================================================*/
#include "kwpserver.h"
#include "../assertor/assertor.h"
#include "../pal/pal_mem.h"
#include "../kwp/kwp_rc.h"
#include "../cdt/cdt.h"
#include "../systime/systime.h"
#include "../kwp/kwp_rc.h"
/*===============================================[ private defines   ]================================================*/

/*===============================================[ private datatypes ]================================================*/

/*===============================================[ private variables ]================================================*/

/*===============================================[ private functions ]================================================*/
static enum kwp_response_code kwpserver_subfunction_not_supported( struct kwp_msg* msg );
static struct kwp_msg *kwpserver_process_request(struct kwpserver_obj *me);

/*================================================[ inline functions ]================================================*/
STATIC_INLINE kwpserver_service_function_t lookup_service_function( struct kwpserver_obj *me , enum kwp_service_identifier sid )
{
    U8 i;

    for( i = 0; i < me->number_of_services; ++i )
    {
        if( me->service_table[i].sid == sid )
        {
            return me->service_table[i].service_func ;
        }
    }

    return kwpserver_subfunction_not_supported;
}

STATIC_INLINE U8 count_services( struct kwpserver_services const* services )
{
   U8 rv=0;
   U8 last_sid=0;
   struct kwpserver_services const* p=services;
   while( 0 != p->sid)
   {
      if( p->sid > last_sid )
      {
         if( NIL == p->service_func )
         {
            return 0;
         }
         last_sid=p->sid;
         rv++;
         p++;
      }
      else
      {
         ASSERTOR_CHK(FALSE,"Service identifier of service table have to be in ascending order ");
         return 0;
      }
   }
   return rv;
}


/*================================================[ public functions ]================================================*/

RC kwpserver_construct( struct kwpserver_obj *me ,struct kwpserver_services const* services, kwpserver_transmit_func_t tx_func, U8 block_size, U8 seperation_time, U16 timeout, U16 pending_time )
{   
   ASSERTOR_PRE( NIL != me,"NIL is not allowed as object pointer." );
   pal_memzero( me, sizeof *me );

   me->service_table=services;
   me->transmit=tx_func;
   me->block_size=block_size;
   me->separation_time=seperation_time;
   me->pending_time=pending_time;
   me->timeout= timeout;

   me->number_of_services=count_services(me->service_table);
   kwptprx_construct( &me->rx_obj, &me->buffer, tx_func, me->block_size, me->separation_time );
   kwptptx_construct( &me->tx_obj, tx_func );
   me->state=kwpserver_state_receiving;

   canmsg_set_dlc(&me->flow_control_msg,8);
   canmsg_set_id(&me->flow_control_msg, me->tx_id);
   canmsg_data_ptr(&me->flow_control_msg)[0]= 0x30;
   canmsg_data_ptr(&me->flow_control_msg)[1]= me->block_size;
   canmsg_data_ptr(&me->flow_control_msg)[2]= me->separation_time;

   return RC_SUCCESS;
}

void kwpserver_process_msg(struct kwpserver_obj *me, U8 *data )
{
   switch( me->state)
   {
      case kwpserver_state_receiving:
      {
         if( kwptprx_process_msg(&me->rx_obj, data ) )
         {
               me->state=kwpserver_state_pending;
               me->pending_timer=systime_get();
         }
         break;
      }
      case kwpserver_state_transmitting:
      {
         kwptptx_process_msg(&me->tx_obj, data);/* probably a flow control frame */
         break;
      }
      case kwpserver_state_pending:
      {
         break;
      }
      default:
      {
         ASSERTOR_ILLEGAL_DEFAULT("Invalid state");
         break;
      }
   }
}

void kwpserver_cyclic_service(struct kwpserver_obj *me)
{
   switch(  me->state)
   {
      case kwpserver_state_transmitting:
      {
         if( kwptptx_cyclic_service(&me->tx_obj) )
         {  /* transmission is done  - start listening to new request */
            me->state=kwpserver_state_receiving;
         }
         break;
      }
      case kwpserver_state_pending:
      {
         if( systime_timeout(me->pending_timer,me->pending_time) )
         {  /* send pending message */
            U8 pending_msg[8] = { 0x02, 0x7f, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00 };
            me->pending_timer = systime_get();
            me->transmit(pending_msg);
         }
         break;
      }
      default:
      {
         break;
      }
   }
}

void kwpserver_idle_service(struct kwpserver_obj *me)
{
   if( kwpserver_state_pending == me->state)
   {
      struct kwp_msg *p_response=kwpserver_process_request(me);

      if(p_response)/* if we want to send an answer */
      {
         if( kwptptx_start_transmission(&me->tx_obj,p_response ) )
         {
            /* transmission is done - waiting for next request */
            me->state = kwpserver_state_receiving;}
         else
         {
            me->state = kwpserver_state_transmitting;
         }
      }
    }
 }
/*===============================================[ private functions ]================================================*/

static struct kwp_msg *kwpserver_process_request(struct kwpserver_obj *me)
{
   struct kwp_msg *p_msg= kwptprx_take_msg(&me->rx_obj);
   if( p_msg)
   {
      enum kwp_service_identifier sid = (enum kwp_service_identifier) p_msg->data[0]; /* read sid from rx message */
      kwpserver_service_function_t service_func = lookup_service_function(me,sid);
      enum kwp_response_code response_code = service_func( p_msg );

      switch( response_code )
      {
         case KWP_RESPONSE_CODE_SUCCESS:
         {
            kwptprx_release_msg(&me->rx_obj);
            p_msg->data[0]= ( sid | 0x40 );
            break;
         }
         case KWP_RESPONSE_CODE_NONE:
         {
            kwptprx_release_msg(&me->rx_obj);
            me->state=kwpserver_state_receiving;
            p_msg=NIL; /* no answer at all */
            break;
         }
         case KWP_RESPONSE_CODE_PENDING:
         {
            p_msg=NIL; /* no answer yet */
            break;
         }
         default:
         {
            /* Error occured */
            p_msg->data[0] = 0x7F;
            p_msg->data[1] = (U8) sid;
            p_msg->data[2] = (U8) response_code;
            p_msg->len = 3;
            kwptprx_release_msg(&me->rx_obj);
            me->state=kwpserver_state_transmitting;
            break;
         }
      }
   }
   return p_msg;

}

/**
*  This function will be called if no valid service function was found.
*/
static enum kwp_response_code kwpserver_subfunction_not_supported( struct kwp_msg* msg )
{
   return KWP_RESPONSE_CODE_SERVICENOTSUPPORTED;
}


/***********************************************************************************************************************
*                             Copyright 2020 Linde Material Handling. All rights reserved.                             *
***********************************************************************************************************************/

